// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using Antlr4.Runtime.Tree;

namespace SnapObjects.Expressions
{
    internal class AndorExpressionVisitor<T, TContext> :
        NodeExpressionVisitor<T, TContext> where TContext : IParseTree
    {
        public AndorExpressionVisitor(IServiceProvider services) : base(services)
        {
        }

        public override T Visit(
            IExpressionVisitor<T> treeVisitor,
            TContext context,
            ParserOptions<T> options)
        {
            var bop = context.GetChild(1).GetText();

            if (this.IsOrOperator(bop) && this.IsAndOperator(bop))
            {
                throw new InternalParseException($"andor: {bop}");
            }

            var left = treeVisitor.Visit(context.GetChild(0), options);

            if (_provider.SupportShortCircuit)
            {
                var leftTest = _provider.ShortCircuit(left);

                if (leftTest && this.IsOrOperator(bop))
                {
                    return _provider.Constant(true, typeof(bool));
                }
                else if (!leftTest && this.IsAndOperator(bop))
                {
                    return _provider.Constant(false, typeof(bool));
                }
                else
                {
                    var right = treeVisitor.Visit(context.GetChild(2), options);

                    var rightTest = _provider.ShortCircuit(right);

                    return this.IsOrOperator(bop)
                        ? _provider.Constant(leftTest || rightTest, typeof(bool))
                        : _provider.Constant(leftTest && rightTest, typeof(bool));
                }
            }
            else
            {
                var right = treeVisitor.Visit(context.GetChild(2), options);

                return this.IsOrOperator(bop)
                    ? _provider.OrElse(left, right, options)
                    : _provider.AndAlso(left, right, options);
            }
        }

        protected virtual bool IsAndOperator(string bop)
        {
            return bop == ExpressionConstants.TOKEN_OP_AND;
        }

        protected virtual bool IsOrOperator(string bop)
        {
            return bop == ExpressionConstants.TOKEN_OP_OR;
        }
    }
}
